Motif/TF assessed
# Load in real data
samples_full = list.files('/Volumes/data/projects/nei/hufnagel/iPSC_RPE_ATAC_Seq/fimo_motifs/', pattern = '*dat.gz', full.names = T)
fimo_data <- list()
for (i in samples_full){
sample <- str_split(str_split(i, '/')[[1]][10], '\\.')[[1]][1]
input <- read_tsv(i)
input <- input %>% mutate(sequence_name = as.character(sequence_name)) %>% mutate(sample = sample)
fimo_data[[sample]] <- input
}
sample_motifs = bind_rows(fimo_data) %>% mutate(sample=as.factor(sample))
#sample_motifs %>% group_by(sample, motif_alt_id) %>% summarise(Count=n()) %>% arrange(-Count)
tf_motif <- sample_motifs %>% select('TF' = `# motif_id`, motif_alt_id) %>% unique()
tf_motif %>% DT::datatable()
# merge sample counts with bootstrap counts
all <- bind_rows(sample_motifs %>% group_by(sample, motif_alt_id) %>% summarise(Count=n()) %>% ungroup() %>% mutate(bootstrap = 'real'),
bootstrap_counts)
# motif ggridges plotter
plotter <- function(motif, scale=TRUE) {
if (scale == TRUE){
all <- all %>% filter(motif_alt_id == !!motif) %>%
group_by(sample, motif_alt_id) %>%
mutate(`Z score` = scale(Count)) %>%
ungroup()
all %>%
mutate(sample = factor(sample, levels=c('iPSC_IIi_9','iPSC_IIJ_10','iPSC_IIK_11','iPSC_IIL_12','RFP_IIE_1','RFP_IIF_2','RFP_IIG_3','RFP_IIH_4','GFP_IIE_5','GFP_IIF_6','GFP_IIG_7','GFP_IIH_8'))) %>%
ggplot(aes(x=`Z score`, y=sample)) +
geom_density_ridges() +
geom_point(data = all %>%
filter(bootstrap == 'real', motif_alt_id == !!motif) %>%
ungroup(), aes(x=`Z score`,y=sample), colour='blue', size=2, alpha=0.5) +
scale_y_discrete(expand = c(0.01, 0)) +
theme_ridges() +
ggtitle(paste0(motif, ' (',
tf_motif %>% filter(motif_alt_id == !!motif) %>% pull(TF),
')'))
}
else {
all %>% filter(motif_alt_id == !!motif) %>%
mutate(sample = factor(sample, levels=c('iPSC_IIi_9','iPSC_IIJ_10','iPSC_IIK_11','iPSC_IIL_12','RFP_IIE_1','RFP_IIF_2','RFP_IIG_3','RFP_IIH_4','GFP_IIE_5','GFP_IIF_6','GFP_IIG_7','GFP_IIH_8'))) %>%
ggplot(aes(x=Count, y=sample)) +
geom_density_ridges() +
geom_point(data = sample_motifs %>%
filter(motif_alt_id == !!motif) %>%
group_by(sample, motif_alt_id) %>%
summarise(Count=n()) %>% ungroup(), aes(x=Count,y=sample), colour='blue', size=2, alpha=0.5) +
scale_y_discrete(expand = c(0.01, 0)) +
theme_ridges() +
ggtitle(paste0(motif, ' (',
tf_motif %>% filter(motif_alt_id == !!motif) %>% pull(TF),
')'))
}
}
DESeq2-based analysis
I realized that motifs by samples is similar to genes by samples
We are dropping RFP II E because from the plots, seems rarely enriched in anything
cts <- all %>% filter(bootstrap=='real') %>% spread(sample, Count) %>% select(-bootstrap, -TF) %>% data.frame()
row.names(cts) <- cts$motif_alt_id
cts %>% head()
cts <- cts[,-1]
coldata <- all %>% select(sample) %>% unique() %>% filter(sample!='RFP_IIE_1') %>% mutate(Type = case_when(grepl('GFP', sample) ~ 'GFP',
grepl('RFP', sample) ~ 'RFP',
TRUE ~ 'iPSC'))
dds <- DESeqDataSetFromMatrix(countData = cts %>% select(one_of(as.character(coldata$sample)) ),
colData = coldata,
design = ~ Type)
some variables in design formula are characters, converting to factors
dds$Type <- relevel(dds$Type, ref='RFP')
dds <- DESeq(dds)
estimating size factors
estimating dispersions
gene-wise dispersion estimates
mean-dispersion relationship
-- note: fitType='parametric', but the dispersion trend was not well captured by the
function: y = a/x + b, and a local regression fit was automatically substituted.
specify fitType='local' or 'mean' to avoid this message next time.
final dispersion estimates
fitting model and testing
res <- results(DESeq(dds))
using pre-existing size factors
estimating dispersions
found already estimated dispersions, replacing these
gene-wise dispersion estimates
mean-dispersion relationship
-- note: fitType='parametric', but the dispersion trend was not well captured by the
function: y = a/x + b, and a local regression fit was automatically substituted.
specify fitType='local' or 'mean' to avoid this message next time.
final dispersion estimates
fitting model and testing
resLFC <- lfcShrink(dds, coef='Type_GFP_vs_RFP', type='apeglm')
using 'apeglm' for LFC shrinkage. If used in published research, please cite:
Zhu, A., Ibrahim, J.G., Love, M.I. (2018) Heavy-tailed prior distributions for
sequence count data: removing the noise and preserving large differences.
bioRxiv. https://doi.org/10.1101/303255
plotMA(resLFC)

plot(hist(resLFC$pvalue))


Table of results
The Z score is the number of standard deviation of motifs found in the sample peaks above the random set of peaks
results <- resLFC %>%
data.frame() %>%
rownames_to_column('motif_alt_id') %>%
left_join(tf_motif) %>%
arrange(pvalue) %>%
data.frame() %>%
left_join(Z_scoring %>% mutate(Type = case_when(grepl('GFP', sample) ~ 'GFP',
grepl('RFP', sample) ~ 'RFP',
TRUE ~ 'iPSC')) %>%
filter(Type=='GFP') %>% group_by(motif_alt_id) %>% summarise(`Z score` = mean(`Z score`))) %>%
left_join(gfp_rfp %>% mutate(TF=Gene, log2FC_GFP_RFP_RNASeq = log2FoldChange) %>% select(TF, log2FC_GFP_RFP_RNASeq))
Joining, by = "motif_alt_id"
Joining, by = "motif_alt_id"
Joining, by = "TF"
results <- resLFC %>%
data.frame() %>%
rownames_to_column('motif_alt_id') %>%
left_join(tf_motif) %>%
arrange(pvalue) %>%
data.frame() %>%
left_join(Z_scoring %>% mutate(Type = case_when(grepl('GFP', sample) ~ 'GFP',
grepl('RFP', sample) ~ 'RFP',
TRUE ~ 'iPSC')) %>%
filter(Type=='GFP') %>% group_by(motif_alt_id) %>% summarise(`Z score` = mean(`Z score`))) %>%
left_join(gfp_rfp %>% mutate(TF=Gene, log2FC_GFP_RFP_RNASeq = log2FoldChange) %>% select(TF, log2FC_GFP_RFP_RNASeq))
Joining, by = "motif_alt_id"
Joining, by = "motif_alt_id"
Joining, by = "TF"
results %>% filter((`Z score`) > 2.5) %>% arrange(pvalue) %>% DT::datatable()
Volcano
volcano_maker <- function(df, title){
df$Class <- 'Not significant'
df$Class[df$padj < 0.01 & df$log2FoldChange > 0.4 & (df$`Z score`) > 2.5] <- "FDR < 0.01 &\nabs(Z score) > 2.5"
df$Class[df$padj < 0.01 & df$log2FoldChange < -0.4 & (df$`Z score`) > 2.5] <- "FDR < 0.01 &\nabs(Z score) > 2.5"
df$Class <- factor(df$Class, levels=c('Not significant', "FDR < 0.01 &\nabs(Z score) > 2.5"))
plot <- ggplot(data=df,aes(x=log2FoldChange,y=-log10(pvalue))) +
geom_point(aes(colour=Class, size=`Z score`), alpha=0.5) +
scale_colour_manual(values=c("gray","darkred")) +
geom_text_repel(data=df %>% filter((padj < 0.01 & log2FoldChange > 0.4 & `Z score` > 2.5) | (padj < 0.01 & log2FoldChange < -0.4 & `Z score` > 2.5)),
aes(label=TF)) +
# geom_vline(aes(xintercept=-0.5),linetype="dotted") +
# geom_vline(aes(xintercept=0.5),linetype="dotted") +
scale_x_continuous(breaks=c(-1,-0.5,0,0.5,1)) +
ggtitle(title) + theme_minimal()
return(plot)
}
volcano_maker(results, 'GFP vs RFP TFBS motif counts')

Plots of the top motifs different between GFP and RFP
RFP II E is plotted here, but not used in the differential analysis above
plots <- list()
for (i in results %>% filter((`Z score`) > 2) %>% arrange(pvalue) %>% head(n=12) %>% pull(motif_alt_id)){
plots[[i]] <- plotter(i, scale=T)
}
plot_grid(plotlist = plots, ncol=3)
Picking joint bandwidth of 0.247
Picking joint bandwidth of 0.26
Picking joint bandwidth of 0.26
Picking joint bandwidth of 0.263
Picking joint bandwidth of 0.252
Picking joint bandwidth of 0.252
Picking joint bandwidth of 0.252
Picking joint bandwidth of 0.268
Picking joint bandwidth of 0.274
Picking joint bandwidth of 0.268
Picking joint bandwidth of 0.258
Picking joint bandwidth of 0.28

# Gene <-> motif matching
# load gtf to map transcript to gene_name
gtf <- read_tsv("/Volumes/data/genomes/1000G_phase2_GRCh37/gencode.v28lift37.metadata.HGNC.gz", col_names = c('Transcript','Gene'))
# gtf <- gtf %>% rowwise() %>% mutate(transcript = ((str_split(V9, ';')[[1]] %>% str_extract('transcript_id.*') %>% na.omit())[1] %>% str_split(., '\"'))[[1]][2],
# gene = ((str_split(V9, ';')[[1]] %>% str_extract('gene_name.*') %>% na.omit())[1] %>% str_split(., '\"'))[[1]][2])
## Sample data
### Closest TSS for OTX2 (M5700_1.02)
# samples_full = list.files('/Volumes/data/projects/nei/hufnagel/iPSC_RPE_ATAC_Seq/closest_TSS_motifs/',
# pattern = '*dat.gz', full.names = T)
#
# closestTSS_data <- list()
# for (i in samples_full){
# sample <- str_split(str_split(i, '/')[[1]][10], '\\.')[[1]][1]
# # input <- read_tsv(i, col_names = c('sequence_name', 'start', 'end', 'motif', 'fimo_pvalue', 'strand', 'tss_seq','tss_start','tss_end', 'transcript', 'blank','tss_strand','coord1','coord2','blank2','exon_num','size','exon_pos','distance'))
# input <- fread(paste('gzcat', i, '| grep M5700_1'))
# colnames(input) <- c('sequence_name', 'start', 'end', 'motif', 'fimo_pvalue', 'strand', 'tss_seq','tss_start','tss_end', 'transcript', 'blank','tss_strand','coord1','coord2','blank2','exon_num','size','exon_pos','distance')
# input <- input %>% mutate(sequence_name = as.character(sequence_name),
# tss_seq = as.character(tss_seq),
# coord1=as.numeric(coord1),
# coord2=as.numeric(coord2),
# blank2 = as.character(blank2),
# exon_num=as.integer(exon_num),
# size=as.numeric(size)) %>% mutate(sample = sample)
# closestTSS_data[[sample]] <- input
# }
# sample_closestTSS = bind_rows(closestTSS_data) %>% mutate(sample=as.factor(sample)) %>% rowwise() %>% mutate(Transcript = str_split(transcript, '_')[[1]][1])
#
# both <- left_join(sample_closestTSS, gtf) %>%
# filter(!is.na(Gene)) %>%
# mutate(motif_loc = paste(sequence_name, start, end, sep='_'))
#
# both %>%
# # remove any TSS over 500kb away
# filter(distance < 500000) %>%
# # one gene per motif
# group_by(motif_loc, sample, Gene) %>%
# top_n(1, distance) %>%
# ungroup() %>%
# # add up to two genes (total) per motif
# group_by(motif_loc, sample) %>%
# top_n(2, distance) %>%
# ungroup() %>%
# # arrange by genes most linked to motif
# group_by(Gene, sample) %>%
# summarise(Count=n(), paste(motif_loc, collapse=', ')) %>%
# arrange(-Count)
# What genes have more PAX6 'associated' motifs compared from GFP to RFP
# enriched_genes <- both %>%
# # only keep one gene per motif
# group_by(motif_loc, sample, Gene) %>%
# top_n(1, distance) %>%
# ungroup() %>%
# # keep up to two genes per motif
# group_by(motif_loc, sample) %>%
# top_n(2, distance) %>%
# ungroup() %>%
# # arrange by genes most linked to motif
# group_by(Gene, sample) %>%
# summarise(Count=n(), paste(motif_loc, collapse=', ')) %>%
# ungroup() %>%
# # collapse to GFP/RFP/iPSC
# mutate(Type = case_when(grepl('GFP', sample) ~ 'GFP',
# grepl('RFP', sample) ~ 'RFP',
# TRUE ~ 'iPSC')) %>%
# group_by(Gene, Type) %>%
# summarise(Total=sum(Count)) %>%
# arrange(-Total) %>%
# spread(Gene, Total) %>% t()
#
# colnames(enriched_genes) <- enriched_genes[1,]
# enriched_genes <- enriched_genes[-1,] %>% data.frame() %>% rownames_to_column('Gene') %>% mutate(GFP = as.numeric(GFP), iPSC = as.numeric(iPSC), RFP = as.numeric((RFP)))
#
# enriched_genes[is.na(enriched_genes)] <- 0
#
# enriched_genes %>% mutate(`deltaGFP <-> RFP` = GFP - RFP) %>% arrange(-`deltaGFP <-> RFP`, GFP) %>% head(1000) %>% DT::datatable(rownames = F)
## does PAX6 regulate PAX6?
#Yes, yes it does.
#GFP specific!
# both %>%
# # only keep one gene per motif
# group_by(motif_loc, sample, Gene) %>%
# top_n(1, distance) %>%
# ungroup() %>%
# # keep up to two genes per motif
# group_by(motif_loc, sample) %>%
# top_n(2, distance) %>%
# ungroup() %>%
# # arrange by genes most linked to motif
# group_by(Gene, sample) %>%
# summarise(Count=n(), `Motif Locations` = paste(motif_loc, collapse=', ')) %>%
# arrange(-Count) %>%
# filter(Gene=='PAX6')
```
LS0tCnRpdGxlOiBNb3RpZiAvIFRGQlMgQW5hbHlzaXMKYXV0aG9yOiBEYXZpZCBNY0dhdWdoZXkKZGF0ZTogJ2ByIGZvcm1hdChTeXMuRGF0ZSgpLCAiJVktJW0tJWQiKWAnCm91dHB1dDogCiAgaHRtbF9ub3RlYm9vazoKICAgIHRoZW1lOiBmbGF0bHkKICAgIHRvYzogdHJ1ZQogICAgY29kZV9mb2xkaW5nOiBoaWRlCi0tLQoKIyBXb3JrZmxvdyB0byBJRCBtb3RpZnMgYW5kIG1hdGNoIHRvIGdlbmVzCgooRnVsbCBpbXBsZW1lbnRhdGlvbiBpbiBTbmFrZWZpbGUpCgoxLiBEb3dubG9hZCBURiBtb3RpZnMgZnJvbSBjaXNicAoyLiBPbmx5IGtlZXAgVEYgd2hpY2ggYXJlIGFicyhsb2dGYykgPiAxIGJldHdlZW4gR0ZQL1JQRSBhbmQgaVBTQyAofjEyMDApCjMuIENoZWNrIGZvciBtb3RpZnMgaW4gbmFycm93IEFUQUMtc2VxIHBlYWtzIHdpdGggZmltbwo0LiBJZGVudGlmeSBjbG9zZXN0IDIgZ2VuZXMgKHVuZGVyIDUwMGsgYnApIHRvIGVhY2ggbW90aWYKNS4gQm9vdHN0cmFwIHN0ZXBzIDIgYW5kIDMgMjUwIHRpbWVzIGVhY2ggdG8gZ2V0IGJhY2tncm91bmQgcmF0ZQoKYGBge3IsIG1lc3NhZ2U9Riwgd2FybmluZz1GLCByZXN1bHRzPSdoaWRlJ30KIyBMb2FkIExpYnJhcmllcyB3aXRob3V0IHByaW50aW5nIGFueSB3YXJuaW5ncyBvciBtZXNzYWdlcwpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeShnZ3JpZGdlcykKbGlicmFyeShjb3dwbG90KQpsaWJyYXJ5KERFU2VxMikKbGlicmFyeShnZ3JlcGVsKQpgYGAKCiMgTW90aWYvVEYgYXNzZXNzZWQKYGBge3IsIHJlc3VsdHMgPSAnaGlkZSd9CiMgTG9hZCBpbiByZWFsIGRhdGEKc2FtcGxlc19mdWxsID0gbGlzdC5maWxlcygnL1ZvbHVtZXMvZGF0YS9wcm9qZWN0cy9uZWkvaHVmbmFnZWwvaVBTQ19SUEVfQVRBQ19TZXEvZmltb19tb3RpZnMvJywgcGF0dGVybiA9ICcqZGF0Lmd6JywgZnVsbC5uYW1lcyA9IFQpCgpmaW1vX2RhdGEgPC0gbGlzdCgpCmZvciAoaSBpbiBzYW1wbGVzX2Z1bGwpewogIHNhbXBsZSA8LSBzdHJfc3BsaXQoc3RyX3NwbGl0KGksICcvJylbWzFdXVsxMF0sICdcXC4nKVtbMV1dWzFdCiAgaW5wdXQgPC0gcmVhZF90c3YoaSkKICBpbnB1dCA8LSBpbnB1dCAlPiUgbXV0YXRlKHNlcXVlbmNlX25hbWUgPSBhcy5jaGFyYWN0ZXIoc2VxdWVuY2VfbmFtZSkpICU+JSBtdXRhdGUoc2FtcGxlID0gc2FtcGxlKQogIGZpbW9fZGF0YVtbc2FtcGxlXV0gPC0gaW5wdXQKfQpzYW1wbGVfbW90aWZzID0gYmluZF9yb3dzKGZpbW9fZGF0YSkgJT4lIG11dGF0ZShzYW1wbGU9YXMuZmFjdG9yKHNhbXBsZSkpCgojc2FtcGxlX21vdGlmcyAlPiUgZ3JvdXBfYnkoc2FtcGxlLCBtb3RpZl9hbHRfaWQpICU+JSBzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUgYXJyYW5nZSgtQ291bnQpCgp0Zl9tb3RpZiA8LSBzYW1wbGVfbW90aWZzICU+JSBzZWxlY3QoJ1RGJyA9IGAjIG1vdGlmX2lkYCwgbW90aWZfYWx0X2lkKSAlPiUgdW5pcXVlKCkKdGZfbW90aWYgJT4lIERUOjpkYXRhdGFibGUoKQpgYGAKCgpgYGB7ciwgd2FybmluZz1GLCBtZXNzYWdlPUYsIGVjaG89RiwgcmVzdWx0cz0naGlkZSd9CiMgTG9hZCBpbiBib290c3RyYXBzCmJvb3RzdHJhcF9zdGF0cyA9IGxpc3QuZmlsZXMoJy9Wb2x1bWVzL2RhdGEvcHJvamVjdHMvbmVpL2h1Zm5hZ2VsL2lQU0NfUlBFX0FUQUNfU2VxL2ZpbW9fbW90aWZzL2Jvb3RzdHJhcHBpbmdfc3RhdHMvJywgcGF0dGVybiA9ICcqZGF0Lmd6JywgZnVsbC5uYW1lcyA9IFQpCiNzYW1wbGVzX2Z1bGwgPSBsaXN0LmZpbGVzKCd+L0Rlc2t0b3AvZmltb19tb3RpZnMvYm9vdHN0cmFwcGluZy8nLCBwYXR0ZXJuID0gJypkYXQuZ3onLCBmdWxsLm5hbWVzID0gVCkKCiMgYmlnIGRpZmZlcmVuY2UgaGVyZSB2cyBhYm92ZQojIG5vdCBzYXZpbmcgZnVsbCBmaWxlLCBhcyBlYWNoIGJvb3RzdHJhcCBpcyB+MjAwbWIgQ09NUFJFU1NFRAojIGp1c3Qgc2F2aW5nIHRoZSBzdGF0cwojIHJpZ2h0IG5vdywgY291bnRzIGJ5IG1vdGlmL3NhbXBsZQpmaW1vX2RhdGEgPC0gbGlzdCgpCmZvciAoaSBpbiBib290c3RyYXBfc3RhdHMpewogIHNhbXBsZSA8LSBzdHJfc3BsaXQoc3RyX3NwbGl0KGksICcvJylbWzFdXVsxMV0sICdcXC4nKVtbMV1dWzFdCiAgYm9vdHN0cmFwX251bSA8LSBzdHJfc3BsaXQoc3RyX3NwbGl0KHN0cl9zcGxpdChpLCAnLycpW1sxXV1bMTFdLCAnXFwuJylbWzFdXVsyXSwgJ18nKVtbMV1dWzJdCiAgc2FtcGxlX2Jvb3QgPC0gcGFzdGUwKHNhbXBsZSwnXycsYm9vdHN0cmFwX251bSkKICBzdGF0cyA8LSByZWFkX3RzdihpKSAlPiUgbXV0YXRlKHNhbXBsZSA9IGFzLmZhY3RvcihzYW1wbGUpLCBib290c3RyYXAgPSBib290c3RyYXBfbnVtKQogIGZpbW9fZGF0YVtbc2FtcGxlX2Jvb3RdXSA8LSBzdGF0cwp9CjEKYm9vdHN0cmFwX2NvdW50cyA9IGJpbmRfcm93cyhmaW1vX2RhdGEpICU+JSBtdXRhdGUoc2FtcGxlPWFzLmZhY3RvcihzYW1wbGUpKSAlPiUgbGVmdF9qb2luKHRmX21vdGlmKQpgYGAKCmBgYHtyfQojIG1lcmdlIHNhbXBsZSBjb3VudHMgd2l0aCBib290c3RyYXAgY291bnRzCmFsbCA8LSBiaW5kX3Jvd3Moc2FtcGxlX21vdGlmcyAlPiUgZ3JvdXBfYnkoc2FtcGxlLCBtb3RpZl9hbHRfaWQpICU+JSBzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUgdW5ncm91cCgpICU+JSBtdXRhdGUoYm9vdHN0cmFwID0gJ3JlYWwnKSwKICAgICAgICAgICAgICAgICBib290c3RyYXBfY291bnRzKQoKIyBtb3RpZiBnZ3JpZGdlcyBwbG90dGVyCgpwbG90dGVyIDwtIGZ1bmN0aW9uKG1vdGlmLCBzY2FsZT1UUlVFKSB7CiAgaWYgKHNjYWxlID09IFRSVUUpewogICAgYWxsIDwtIGFsbCAlPiUgZmlsdGVyKG1vdGlmX2FsdF9pZCA9PSAhIW1vdGlmKSAlPiUgCiAgICAgIGdyb3VwX2J5KHNhbXBsZSwgbW90aWZfYWx0X2lkKSAlPiUgCiAgICAgIG11dGF0ZShgWiBzY29yZWAgPSBzY2FsZShDb3VudCkpICU+JSAKICAgICAgdW5ncm91cCgpIAogICAgYWxsICU+JSAKICAgICAgbXV0YXRlKHNhbXBsZSA9IGZhY3RvcihzYW1wbGUsIGxldmVscz1jKCdpUFNDX0lJaV85JywnaVBTQ19JSUpfMTAnLCdpUFNDX0lJS18xMScsJ2lQU0NfSUlMXzEyJywnUkZQX0lJRV8xJywnUkZQX0lJRl8yJywnUkZQX0lJR18zJywnUkZQX0lJSF80JywnR0ZQX0lJRV81JywnR0ZQX0lJRl82JywnR0ZQX0lJR183JywnR0ZQX0lJSF84JykpKSAlPiUgCiAgICAgIGdncGxvdChhZXMoeD1gWiBzY29yZWAsIHk9c2FtcGxlKSkgKyAKICAgICAgZ2VvbV9kZW5zaXR5X3JpZGdlcygpICsKICAgICAgZ2VvbV9wb2ludChkYXRhID0gYWxsICU+JSAKICAgICAgICAgICAgICAgICAgIGZpbHRlcihib290c3RyYXAgPT0gJ3JlYWwnLCBtb3RpZl9hbHRfaWQgPT0gISFtb3RpZikgJT4lIAogICAgICAgICAgICAgICAgICAgdW5ncm91cCgpLCBhZXMoeD1gWiBzY29yZWAseT1zYW1wbGUpLCBjb2xvdXI9J2JsdWUnLCBzaXplPTIsIGFscGhhPTAuNSkgKwogICAgICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMC4wMSwgMCkpICsKICAgICAgdGhlbWVfcmlkZ2VzKCkgKyAKICAgICAgZ2d0aXRsZShwYXN0ZTAobW90aWYsICcgKCcsIAogICAgICAgICAgICAgICAgICAgICB0Zl9tb3RpZiAlPiUgZmlsdGVyKG1vdGlmX2FsdF9pZCA9PSAhIW1vdGlmKSAlPiUgcHVsbChURiksCiAgICAgICAgICAgICAgICAgICAgICcpJykpCiAgfQogIGVsc2UgewogICAgYWxsICU+JSBmaWx0ZXIobW90aWZfYWx0X2lkID09ICEhbW90aWYpICU+JSAKICAgICAgbXV0YXRlKHNhbXBsZSA9IGZhY3RvcihzYW1wbGUsIGxldmVscz1jKCdpUFNDX0lJaV85JywnaVBTQ19JSUpfMTAnLCdpUFNDX0lJS18xMScsJ2lQU0NfSUlMXzEyJywnUkZQX0lJRV8xJywnUkZQX0lJRl8yJywnUkZQX0lJR18zJywnUkZQX0lJSF80JywnR0ZQX0lJRV81JywnR0ZQX0lJRl82JywnR0ZQX0lJR183JywnR0ZQX0lJSF84JykpKSAlPiUgCiAgICAgIGdncGxvdChhZXMoeD1Db3VudCwgeT1zYW1wbGUpKSArIAogICAgICBnZW9tX2RlbnNpdHlfcmlkZ2VzKCkgKwogICAgICBnZW9tX3BvaW50KGRhdGEgPSBzYW1wbGVfbW90aWZzICU+JSAKICAgICAgICAgICAgICAgICAgIGZpbHRlcihtb3RpZl9hbHRfaWQgPT0gISFtb3RpZikgJT4lIAogICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoc2FtcGxlLCBtb3RpZl9hbHRfaWQpICU+JQogICAgICAgICAgICAgICAgICAgc3VtbWFyaXNlKENvdW50PW4oKSkgJT4lIHVuZ3JvdXAoKSwgYWVzKHg9Q291bnQseT1zYW1wbGUpLCBjb2xvdXI9J2JsdWUnLCBzaXplPTIsIGFscGhhPTAuNSkgKwogICAgICBzY2FsZV95X2Rpc2NyZXRlKGV4cGFuZCA9IGMoMC4wMSwgMCkpICsKICAgICAgdGhlbWVfcmlkZ2VzKCkgKyAKICAgICAgZ2d0aXRsZShwYXN0ZTAobW90aWYsICcgKCcsIAogICAgICAgICAgICAgICAgICAgICB0Zl9tb3RpZiAlPiUgZmlsdGVyKG1vdGlmX2FsdF9pZCA9PSAhIW1vdGlmKSAlPiUgcHVsbChURiksCiAgICAgICAgICAgICAgICAgICAgICcpJykpCiAgfQp9CmBgYAojIFBsb3QgZW5yaWNobWVudCAgc3BlY2lmaWMgbW90aWYvVEZCUwoKIyMgVEVUMQpgYGB7cn0KcGxvdHRlcignTTA2MTBfMS4wMicpCmBgYAoKIyMgRE5NVDEKYGBge3J9CnBsb3R0ZXIoJ00wNjA5XzEuMDInKQpgYGAKCiMjIFBBWDYKYGBge3J9CnBsb3R0ZXIoJ002NDEwXzEuMDInKQpgYGAKCiMjIFNPWDEwCmBgYHtyfQpwbG90dGVyKCdNNjQ3MF8xLjAyJykKYGBgCgojIyBNSVRGCmBgYHtyfQpwbG90dGVyKCdNNjM0NV8xLjAyJykKYGBgCgojIyBIRVMxCmBgYHtyfQpwbG90dGVyKCdNNjI3MV8xLjAyJykKYGBgCgojIyBTSVgzCmBgYHtyfQpwbG90dGVyKCdNMTAxNl8xLjAyJykKYGBgCgojIyBTSVgyCmBgYHtyfQpwbG90dGVyKCdNMDk1Ml8xLjAyJykKYGBgCgojIERpc3RyaWJ1dGlvbiBvZiBURkJTL21vdGlmIGVucmljaG1lbnQKIyMgRm9sZCBDaGFuZ2UKYGBge3J9CmRlbHRhX21vdGlmX0ZDIDwtIGFsbCAlPiUgbXV0YXRlKFR5cGUgPSBjYXNlX3doZW4oZ3JlcGwoJ0dGUCcsIHNhbXBsZSkgfiAnR0ZQJywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCdSRlAnLCBzYW1wbGUpIH4gJ1JGUCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gJ2lQU0MnKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBCb290ID0gY2FzZV93aGVuKGJvb3RzdHJhcCA9PSAncmVhbCcgfiAncmVhbCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gJ2Jvb3QnKSkgJT4lIAogIGdyb3VwX2J5KFR5cGUsIG1vdGlmX2FsdF9pZCwgQm9vdCkgJT4lIAogIG11dGF0ZShgWiBzY29yZWAgPSBzY2FsZShDb3VudCkpICU+JSAKICBzdW1tYXJpc2UoTWVhbj1tZWFuKENvdW50KSkgJT4lIAogIHNwcmVhZChCb290LCBNZWFuKSAlPiUKICB1bmdyb3VwKCkgJT4lIAogIG11dGF0ZShGQyA9IHJlYWwgLyBib290LCBUeXBlID0gZmFjdG9yKFR5cGUsIGxldmVscz1jKCdpUFNDJywnUkZQJywnR0ZQJykpKSAlPiUgCiAgYXJyYW5nZSgtRkMpICU+JSAKICBsZWZ0X2pvaW4odGZfbW90aWYpICU+JSAKICBzZWxlY3QoLWJvb3QsIC1yZWFsKSAKCmRlbHRhX21vdGlmX0ZDICU+JSBnZ3Bsb3QoYWVzKHk9VHlwZSwgeD1sb2cyKEZDKSkpICsgZ2VvbV9kZW5zaXR5X3JpZGdlcygpICsgdGhlbWVfcmlkZ2VzKCkgCgpkZWx0YV9tb3RpZl9GQyAlPiUgZ2dwbG90KGFlcyh5PVR5cGUsIHg9RkMpKSArIGdlb21fZGVuc2l0eV9yaWRnZXMoKSArIHRoZW1lX3JpZGdlcygpIApgYGAKCgoKYGBge3J9CiMgY2FsY3VsYXRlIHogc2NvcmVzIGZvciBhbGwKWl9zY29yaW5nIDwtIGFsbCAlPiUgCiAgIyBjb2xsYXBzZSBieSBUeXBlLCBtb3RpZiwgYW5kIHdoZXRoZXIgYm9vdHN0cmFwIG9yIHJlYWwKICBncm91cF9ieShzYW1wbGUsIG1vdGlmX2FsdF9pZCkgJT4lIAogIG11dGF0ZShgWiBzY29yZWAgPSBzY2FsZShDb3VudCkpICU+JSAKICBmaWx0ZXIoYm9vdHN0cmFwID09ICdyZWFsJykgJT4lIAogIHNlbGVjdCgtYm9vdHN0cmFwLCAtVEYpICU+JSAKICBsZWZ0X2pvaW4odGZfbW90aWYpCgpaX3Njb3JpbmcgJT4lIGhlYWQoKQpgYGAKIyBEaXN0cmlidXRpb24gb2YgKnJlbGF0aXZlKiBURkJTL21vdGlmIGVucmljaG1lbnQgKmJldHdlZW4qIEdGUCA8LT4gUkZQCmBgYHtyfQpGQ19tb3RpZl9GQyA8LSBhbGwgJT4lIG11dGF0ZShUeXBlID0gY2FzZV93aGVuKGdyZXBsKCdHRlAnLCBzYW1wbGUpIH4gJ0dGUCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgnUkZQJywgc2FtcGxlKSB+ICdSRlAnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICdpUFNDJyksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgQm9vdCA9IGNhc2Vfd2hlbihib290c3RyYXAgPT0gJ3JlYWwnIH4gJ3JlYWwnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICdib290JykpICU+JSAKICAjIGNvbGxhcHNlIGJ5IFR5cGUsIG1vdGlmLCBhbmQgd2hldGhlciBib290c3RyYXAgb3IgcmVhbAogIGdyb3VwX2J5KFR5cGUsIG1vdGlmX2FsdF9pZCwgQm9vdCkgJT4lIAogIHN1bW1hcmlzZShNZWFuPW1lYW4oQ291bnQpKSAlPiUgCiAgIyBnbyB3aWRlCiAgc3ByZWFkKEJvb3QsIE1lYW4pICU+JQogICMgc28geW91IGNhbiBjYWxjdWxhdGUgRkMKICBtdXRhdGUoRkMgPSByZWFsIC8gYm9vdCkgJT4lIAogIGFycmFuZ2UoLUZDKSAlPiUgCiAgIyBhZGQgbW90aWYgbmFtZQogIGxlZnRfam9pbih0Zl9tb3RpZikgJT4lIAogIHNlbGVjdCgtYm9vdCwgLXJlYWwpICU+JQogICMgc3ByZWFkIGFnYWluIHRvIGFsbG93IGZvciBHRlAgdnMgUkZQIG9yIGlQU0MgY29tcGFyaXNvbnMKICBzcHJlYWQoVHlwZSwgRkMpICU+JSAKICBmaWx0ZXIoR0ZQID4gMS4yKSAlPiUgCiAgbXV0YXRlKGBGQ14yIEdGUCA8LT4gaVBTQ2AgPSBHRlAgLyBpUFNDLCBgRkNeMiBHRlAgPC0+IFJGUGAgPSBHRlAgLyBSRlApICU+JSAKICBhcnJhbmdlKC1gRkNeMiBHRlAgPC0+IFJGUGApIAoKcGxvdDEgPC0gRkNfbW90aWZfRkMgJT4lIAogIGdncGxvdChhZXMoeD1gRkNeMiBHRlAgPC0+IFJGUGApKSArIAogIGdlb21fZGVuc2l0eShmaWxsPSdncmF5JykgKyAKICB0aGVtZV9taW5pbWFsKCkgKyAKICBnZW9tX3ZsaW5lKHhpbnRlcmNlcHQgPSAxLjIsIGNvbG9yPSdibHVlJykKcGxvdDEKYGBgCgojIERpc3RyaWJ1dGlvbiBvZiAqcmVsYXRpdmUqIFRGQlMvbW90aWYgZW5yaWNobWVudCAqYmV0d2VlbiogR0ZQIDwtPiBpUFNDCmBgYHtyfQpwbG90MiA8LSBGQ19tb3RpZl9GQyAlPiUgCiAgZ2dwbG90KGFlcyh4PWBGQ14yIEdGUCA8LT4gaVBTQ2ApKSArIAogIGdlb21fZGVuc2l0eShmaWxsPSdncmF5JykgKyAKICB0aGVtZV9taW5pbWFsKCkrIAogIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IDEuMiwgY29sb3IgPSAnYmx1ZScpCnBsb3QyIApgYGAKCiMjIEJvdGggdG9nZXRoZXIKYGBge3J9CnBsb3RfZ3JpZChwbG90MSArIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoMC41LDUuNSkpLCBwbG90MiArIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoMC41LDUuNSkpLCBucm93ID0gMiwgYWxpZ24gPSAnaHYnLCBheGlzPSd0YmxyJykKYGBgCgojIERFU2VxMi1iYXNlZCBhbmFseXNpcwpJIHJlYWxpemVkIHRoYXQgbW90aWZzIGJ5IHNhbXBsZXMgaXMgc2ltaWxhciB0byBnZW5lcyBieSBzYW1wbGVzCgpXZSBhcmUgZHJvcHBpbmcgUkZQIElJIEUgYmVjYXVzZSBmcm9tIHRoZSBwbG90cywgc2VlbXMgcmFyZWx5IGVucmljaGVkIGluIGFueXRoaW5nCmBgYHtyfQpjdHMgPC0gYWxsICU+JSBmaWx0ZXIoYm9vdHN0cmFwPT0ncmVhbCcpICU+JSBzcHJlYWQoc2FtcGxlLCBDb3VudCkgJT4lIHNlbGVjdCgtYm9vdHN0cmFwLCAtVEYpICU+JSBkYXRhLmZyYW1lKCkKcm93Lm5hbWVzKGN0cykgPC0gY3RzJG1vdGlmX2FsdF9pZApjdHMgJT4lIGhlYWQoKQpjdHMgPC0gY3RzWywtMV0KCmNvbGRhdGEgPC0gYWxsICU+JSBzZWxlY3Qoc2FtcGxlKSAlPiUgdW5pcXVlKCkgJT4lIGZpbHRlcihzYW1wbGUhPSdSRlBfSUlFXzEnKSAlPiUgbXV0YXRlKFR5cGUgPSAgY2FzZV93aGVuKGdyZXBsKCdHRlAnLCBzYW1wbGUpIH4gJ0dGUCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBncmVwbCgnUkZQJywgc2FtcGxlKSB+ICdSRlAnLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgVFJVRSB+ICdpUFNDJykpCgpkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBjdHMgJT4lIHNlbGVjdChvbmVfb2YoYXMuY2hhcmFjdGVyKGNvbGRhdGEkc2FtcGxlKSkgKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGNvbGRhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gVHlwZSkKZGRzJFR5cGUgPC0gcmVsZXZlbChkZHMkVHlwZSwgcmVmPSdSRlAnKQpkZHMgPC0gREVTZXEoZGRzKQoKcmVzIDwtIHJlc3VsdHMoREVTZXEoZGRzKSkKcmVzTEZDIDwtIGxmY1NocmluayhkZHMsIGNvZWY9J1R5cGVfR0ZQX3ZzX1JGUCcsIHR5cGU9J2FwZWdsbScpCnBsb3RNQShyZXNMRkMpCnBsb3QoaGlzdChyZXNMRkMkcHZhbHVlKSkKCmBgYAoKYGBge3J9CiMgTG9hZCBpbiBSTkEtc2VxIGRhdGEKZ2ZwX3JmcCA8LSByZWFkX2Nzdignfi9naXQvaXBzY19ycGVfUk5BLXNlcS9kYXRhL0dGUF92c19SRlAucmVzdWx0cy5jc3YnKQpycGVfaXBzYyA8LSByZWFkX2Nzdignfi9naXQvaXBzY19ycGVfUk5BLXNlcS9kYXRhL1JQRV92c19pUFNDLnJlc3VsdHMuY3N2JykKZ2ZwX3JmcCAlPiUgaGVhZCgpCmBgYAoKIyMgVGFibGUgb2YgcmVzdWx0cwpUaGUgYFogc2NvcmVgIGlzIHRoZSBudW1iZXIgb2Ygc3RhbmRhcmQgZGV2aWF0aW9uIG9mIG1vdGlmcyBmb3VuZCBpbiB0aGUgc2FtcGxlIHBlYWtzIGFib3ZlIHRoZSByYW5kb20gc2V0IG9mIHBlYWtzCmBgYHtyfQpyZXN1bHRzIDwtIHJlc0xGQyAlPiUgCiAgZGF0YS5mcmFtZSgpICU+JSAKICByb3duYW1lc190b19jb2x1bW4oJ21vdGlmX2FsdF9pZCcpICU+JSAKICBsZWZ0X2pvaW4odGZfbW90aWYpICU+JSAKICBhcnJhbmdlKHB2YWx1ZSkgJT4lIAogIGRhdGEuZnJhbWUoKSAlPiUgCiAgbGVmdF9qb2luKFpfc2NvcmluZyAlPiUgIG11dGF0ZShUeXBlID0gY2FzZV93aGVuKGdyZXBsKCdHRlAnLCBzYW1wbGUpIH4gJ0dGUCcsCiAgICAgICAgICAgICAgICAgICAgICAgICAgZ3JlcGwoJ1JGUCcsIHNhbXBsZSkgfiAnUkZQJywKICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gJ2lQU0MnKSkgJT4lIAogICAgICAgICAgICAgIGZpbHRlcihUeXBlPT0nR0ZQJykgJT4lIGdyb3VwX2J5KG1vdGlmX2FsdF9pZCkgJT4lIHN1bW1hcmlzZShgWiBzY29yZWAgPSBtZWFuKGBaIHNjb3JlYCkpKSAlPiUgCiAgbGVmdF9qb2luKGdmcF9yZnAgJT4lIG11dGF0ZShURj1HZW5lLCBsb2cyRkNfR0ZQX1JGUF9STkFTZXEgPSBsb2cyRm9sZENoYW5nZSkgJT4lIHNlbGVjdChURiwgbG9nMkZDX0dGUF9SRlBfUk5BU2VxKSkgCnJlc3VsdHMgJT4lIGZpbHRlcigoYFogc2NvcmVgKSA+IDIuNSkgJT4lIGFycmFuZ2UocHZhbHVlKSAlPiUgRFQ6OmRhdGF0YWJsZSgpCmBgYAoKIyMgVm9sY2FubwpgYGB7cn0Kdm9sY2Fub19tYWtlciA8LSBmdW5jdGlvbihkZiwgdGl0bGUpewogIGRmJENsYXNzIDwtICdOb3Qgc2lnbmlmaWNhbnQnCiAgZGYkQ2xhc3NbZGYkcGFkaiA8IDAuMDEgJiBkZiRsb2cyRm9sZENoYW5nZSA+IDAuNCAmIChkZiRgWiBzY29yZWApID4gMi41XSA8LSAiRkRSIDwgMC4wMSAmXG5hYnMoWiBzY29yZSkgPiAyLjUiCiAgZGYkQ2xhc3NbZGYkcGFkaiA8IDAuMDEgJiBkZiRsb2cyRm9sZENoYW5nZSA8IC0wLjQgJiAoZGYkYFogc2NvcmVgKSA+IDIuNV0gPC0gIkZEUiA8IDAuMDEgJlxuYWJzKFogc2NvcmUpID4gMi41IgogIGRmJENsYXNzIDwtIGZhY3RvcihkZiRDbGFzcywgbGV2ZWxzPWMoJ05vdCBzaWduaWZpY2FudCcsICJGRFIgPCAwLjAxICZcbmFicyhaIHNjb3JlKSA+IDIuNSIpKQogIHBsb3QgPC0gZ2dwbG90KGRhdGE9ZGYsYWVzKHg9bG9nMkZvbGRDaGFuZ2UseT0tbG9nMTAocHZhbHVlKSkpICsgCiAgICBnZW9tX3BvaW50KGFlcyhjb2xvdXI9Q2xhc3MsIHNpemU9YFogc2NvcmVgKSwgYWxwaGE9MC41KSArCiAgICBzY2FsZV9jb2xvdXJfbWFudWFsKHZhbHVlcz1jKCJncmF5IiwiZGFya3JlZCIpKSArIAogICAgZ2VvbV90ZXh0X3JlcGVsKGRhdGE9ZGYgJT4lIGZpbHRlcigocGFkaiA8IDAuMDEgJiBsb2cyRm9sZENoYW5nZSA+IDAuNCAmIGBaIHNjb3JlYCA+IDIuNSkgfCAocGFkaiA8IDAuMDEgJiBsb2cyRm9sZENoYW5nZSA8IC0wLjQgJiBgWiBzY29yZWAgPiAyLjUpKSwgCiAgICAgICAgICAgICAgICAgICAgYWVzKGxhYmVsPVRGKSkgKwogICAgIyBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0PS0wLjUpLGxpbmV0eXBlPSJkb3R0ZWQiKSArCiAgICAjIGdlb21fdmxpbmUoYWVzKHhpbnRlcmNlcHQ9MC41KSxsaW5ldHlwZT0iZG90dGVkIikgKwogICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcz1jKC0xLC0wLjUsMCwwLjUsMSkpICsKICAgIGdndGl0bGUodGl0bGUpICsgdGhlbWVfbWluaW1hbCgpCiAgcmV0dXJuKHBsb3QpCn0Kdm9sY2Fub19tYWtlcihyZXN1bHRzLCAnR0ZQIHZzIFJGUCBURkJTIG1vdGlmIGNvdW50cycpCgpgYGAKCgojIyBQbG90cyBvZiB0aGUgdG9wIG1vdGlmcyBkaWZmZXJlbnQgYmV0d2VlbiBHRlAgYW5kIFJGUApSRlAgSUkgRSBpcyBwbG90dGVkIGhlcmUsIGJ1dCBub3QgdXNlZCBpbiB0aGUgZGlmZmVyZW50aWFsIGFuYWx5c2lzIGFib3ZlCmBgYHtyLCBmaWcuaGVpZ2h0PTUsIGZpZy53aWR0aD02fQpwbG90cyA8LSBsaXN0KCkKZm9yIChpIGluIHJlc3VsdHMgJT4lIGZpbHRlcigoYFogc2NvcmVgKSA+IDIpICU+JSBhcnJhbmdlKHB2YWx1ZSkgJT4lIGhlYWQobj0xMikgJT4lIHB1bGwobW90aWZfYWx0X2lkKSl7CiAgcGxvdHNbW2ldXSA8LSBwbG90dGVyKGksIHNjYWxlPVQpCn0KCgpwbG90X2dyaWQocGxvdGxpc3QgPSBwbG90cywgbmNvbD0zKQpgYGAKCgpgYGB7cn0KIyBHZW5lIDwtPiBtb3RpZiBtYXRjaGluZwojIGxvYWQgZ3RmIHRvIG1hcCB0cmFuc2NyaXB0IHRvIGdlbmVfbmFtZQoKZ3RmIDwtIHJlYWRfdHN2KCIvVm9sdW1lcy9kYXRhL2dlbm9tZXMvMTAwMEdfcGhhc2UyX0dSQ2gzNy9nZW5jb2RlLnYyOGxpZnQzNy5tZXRhZGF0YS5IR05DLmd6IiwgY29sX25hbWVzID0gYygnVHJhbnNjcmlwdCcsJ0dlbmUnKSkKIyBndGYgPC0gZ3RmICU+JSByb3d3aXNlKCkgJT4lIG11dGF0ZSh0cmFuc2NyaXB0ID0gKChzdHJfc3BsaXQoVjksICc7JylbWzFdXSAlPiUgc3RyX2V4dHJhY3QoJ3RyYW5zY3JpcHRfaWQuKicpICU+JSBuYS5vbWl0KCkpWzFdICU+JSBzdHJfc3BsaXQoLiwgJ1wiJykpW1sxXV1bMl0sCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBnZW5lID0gKChzdHJfc3BsaXQoVjksICc7JylbWzFdXSAlPiUgc3RyX2V4dHJhY3QoJ2dlbmVfbmFtZS4qJykgJT4lIG5hLm9taXQoKSlbMV0gJT4lIHN0cl9zcGxpdCguLCAnXCInKSlbWzFdXVsyXSkKYGBgCgpgYGB7cn0KIyMgU2FtcGxlIGRhdGEKIyMjIENsb3Nlc3QgVFNTIGZvciBPVFgyIChNNTcwMF8xLjAyKQoKIyBzYW1wbGVzX2Z1bGwgPSBsaXN0LmZpbGVzKCcvVm9sdW1lcy9kYXRhL3Byb2plY3RzL25laS9odWZuYWdlbC9pUFNDX1JQRV9BVEFDX1NlcS9jbG9zZXN0X1RTU19tb3RpZnMvJywgCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBwYXR0ZXJuID0gJypkYXQuZ3onLCBmdWxsLm5hbWVzID0gVCkKIyAKIyBjbG9zZXN0VFNTX2RhdGEgPC0gbGlzdCgpCiMgZm9yIChpIGluIHNhbXBsZXNfZnVsbCl7CiMgICBzYW1wbGUgPC0gc3RyX3NwbGl0KHN0cl9zcGxpdChpLCAnLycpW1sxXV1bMTBdLCAnXFwuJylbWzFdXVsxXQojICAjIGlucHV0IDwtIHJlYWRfdHN2KGksIGNvbF9uYW1lcyA9IGMoJ3NlcXVlbmNlX25hbWUnLCAnc3RhcnQnLCAnZW5kJywgJ21vdGlmJywgJ2ZpbW9fcHZhbHVlJywgJ3N0cmFuZCcsICd0c3Nfc2VxJywndHNzX3N0YXJ0JywndHNzX2VuZCcsICd0cmFuc2NyaXB0JywgICdibGFuaycsJ3Rzc19zdHJhbmQnLCdjb29yZDEnLCdjb29yZDInLCdibGFuazInLCdleG9uX251bScsJ3NpemUnLCdleG9uX3BvcycsJ2Rpc3RhbmNlJykpCiMgICBpbnB1dCA8LSBmcmVhZChwYXN0ZSgnZ3pjYXQnLCBpLCAnfCBncmVwIE01NzAwXzEnKSkKIyAgIGNvbG5hbWVzKGlucHV0KSA8LSBjKCdzZXF1ZW5jZV9uYW1lJywgJ3N0YXJ0JywgJ2VuZCcsICdtb3RpZicsICdmaW1vX3B2YWx1ZScsICdzdHJhbmQnLCAndHNzX3NlcScsJ3Rzc19zdGFydCcsJ3Rzc19lbmQnLCAndHJhbnNjcmlwdCcsICAnYmxhbmsnLCd0c3Nfc3RyYW5kJywnY29vcmQxJywnY29vcmQyJywnYmxhbmsyJywnZXhvbl9udW0nLCdzaXplJywnZXhvbl9wb3MnLCdkaXN0YW5jZScpCiMgICBpbnB1dCA8LSBpbnB1dCAlPiUgbXV0YXRlKHNlcXVlbmNlX25hbWUgPSBhcy5jaGFyYWN0ZXIoc2VxdWVuY2VfbmFtZSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRzc19zZXEgPSBhcy5jaGFyYWN0ZXIodHNzX3NlcSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvb3JkMT1hcy5udW1lcmljKGNvb3JkMSksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvb3JkMj1hcy5udW1lcmljKGNvb3JkMiksCiMgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGJsYW5rMiA9IGFzLmNoYXJhY3RlcihibGFuazIpLAojICAgICAgICAgICAgICAgICAgICAgICAgICAgICBleG9uX251bT1hcy5pbnRlZ2VyKGV4b25fbnVtKSwKIyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2l6ZT1hcy5udW1lcmljKHNpemUpKSAlPiUgbXV0YXRlKHNhbXBsZSA9IHNhbXBsZSkKIyAgIGNsb3Nlc3RUU1NfZGF0YVtbc2FtcGxlXV0gPC0gaW5wdXQKIyB9CiMgc2FtcGxlX2Nsb3Nlc3RUU1MgPSBiaW5kX3Jvd3MoY2xvc2VzdFRTU19kYXRhKSAlPiUgbXV0YXRlKHNhbXBsZT1hcy5mYWN0b3Ioc2FtcGxlKSkgJT4lIHJvd3dpc2UoKSAlPiUgbXV0YXRlKFRyYW5zY3JpcHQgPSBzdHJfc3BsaXQodHJhbnNjcmlwdCwgJ18nKVtbMV1dWzFdKQojIAojIGJvdGggPC0gbGVmdF9qb2luKHNhbXBsZV9jbG9zZXN0VFNTLCBndGYpICU+JSAKIyAgIGZpbHRlcighaXMubmEoR2VuZSkpICU+JSAKIyAgIG11dGF0ZShtb3RpZl9sb2MgPSBwYXN0ZShzZXF1ZW5jZV9uYW1lLCBzdGFydCwgZW5kLCBzZXA9J18nKSkKIyAKIyBib3RoICU+JSAKIyAgICMgcmVtb3ZlIGFueSBUU1Mgb3ZlciA1MDBrYiBhd2F5CiMgICBmaWx0ZXIoZGlzdGFuY2UgPCA1MDAwMDApICU+JSAKIyAgICMgIG9uZSBnZW5lIHBlciBtb3RpZgojICAgZ3JvdXBfYnkobW90aWZfbG9jLCBzYW1wbGUsIEdlbmUpICU+JSAKIyAgIHRvcF9uKDEsIGRpc3RhbmNlKSAlPiUgCiMgICB1bmdyb3VwKCkgJT4lIAojICAgIyBhZGQgdXAgdG8gdHdvIGdlbmVzICh0b3RhbCkgcGVyIG1vdGlmCiMgICBncm91cF9ieShtb3RpZl9sb2MsIHNhbXBsZSkgJT4lIAojICAgdG9wX24oMiwgZGlzdGFuY2UpICU+JSAKIyAgIHVuZ3JvdXAoKSAlPiUgCiMgICAjIGFycmFuZ2UgYnkgZ2VuZXMgbW9zdCBsaW5rZWQgdG8gbW90aWYgIAojICAgZ3JvdXBfYnkoR2VuZSwgc2FtcGxlKSAlPiUgCiMgICBzdW1tYXJpc2UoQ291bnQ9bigpLCBwYXN0ZShtb3RpZl9sb2MsIGNvbGxhcHNlPScsICcpKSAlPiUgCiMgICBhcnJhbmdlKC1Db3VudCkKYGBgCgpgYGB7cn0KCiMgV2hhdCBnZW5lcyBoYXZlIG1vcmUgUEFYNiAnYXNzb2NpYXRlZCcgbW90aWZzIGNvbXBhcmVkIGZyb20gR0ZQIHRvIFJGUAoKIyBlbnJpY2hlZF9nZW5lcyA8LSBib3RoICU+JSAKIyAgICMgb25seSBrZWVwIG9uZSBnZW5lIHBlciBtb3RpZgojICAgZ3JvdXBfYnkobW90aWZfbG9jLCBzYW1wbGUsIEdlbmUpICU+JSAKIyAgIHRvcF9uKDEsIGRpc3RhbmNlKSAlPiUgCiMgICB1bmdyb3VwKCkgJT4lIAojICAgIyBrZWVwIHVwIHRvIHR3byBnZW5lcyBwZXIgbW90aWYKIyAgIGdyb3VwX2J5KG1vdGlmX2xvYywgc2FtcGxlKSAlPiUgCiMgICB0b3BfbigyLCBkaXN0YW5jZSkgJT4lIAojICAgdW5ncm91cCgpICU+JSAKIyAgICMgYXJyYW5nZSBieSBnZW5lcyBtb3N0IGxpbmtlZCB0byBtb3RpZiAgCiMgICBncm91cF9ieShHZW5lLCBzYW1wbGUpICU+JSAKIyAgIHN1bW1hcmlzZShDb3VudD1uKCksIHBhc3RlKG1vdGlmX2xvYywgY29sbGFwc2U9JywgJykpICU+JSAKIyAgIHVuZ3JvdXAoKSAlPiUgCiMgICAjIGNvbGxhcHNlIHRvIEdGUC9SRlAvaVBTQwojICAgbXV0YXRlKFR5cGUgPSBjYXNlX3doZW4oZ3JlcGwoJ0dGUCcsIHNhbXBsZSkgfiAnR0ZQJywKIyAgICAgICAgICAgICAgICAgICAgICAgICAgIGdyZXBsKCdSRlAnLCBzYW1wbGUpIH4gJ1JGUCcsCiMgICAgICAgICAgICAgICAgICAgICAgICAgICBUUlVFIH4gJ2lQU0MnKSkgJT4lIAojICAgZ3JvdXBfYnkoR2VuZSwgVHlwZSkgJT4lIAojICAgc3VtbWFyaXNlKFRvdGFsPXN1bShDb3VudCkpICU+JSAKIyAgIGFycmFuZ2UoLVRvdGFsKSAlPiUgCiMgICBzcHJlYWQoR2VuZSwgVG90YWwpICU+JSB0KCkgCiMgCiMgY29sbmFtZXMoZW5yaWNoZWRfZ2VuZXMpIDwtIGVucmljaGVkX2dlbmVzWzEsXQojIGVucmljaGVkX2dlbmVzIDwtIGVucmljaGVkX2dlbmVzWy0xLF0gJT4lIGRhdGEuZnJhbWUoKSAlPiUgcm93bmFtZXNfdG9fY29sdW1uKCdHZW5lJykgJT4lICBtdXRhdGUoR0ZQID0gYXMubnVtZXJpYyhHRlApLCBpUFNDID0gYXMubnVtZXJpYyhpUFNDKSwgUkZQID0gYXMubnVtZXJpYygoUkZQKSkpCiMgCiMgZW5yaWNoZWRfZ2VuZXNbaXMubmEoZW5yaWNoZWRfZ2VuZXMpXSA8LSAwCiMgCiMgZW5yaWNoZWRfZ2VuZXMgJT4lIG11dGF0ZShgZGVsdGFHRlAgPC0+IFJGUGAgPSBHRlAgLSBSRlApICU+JSBhcnJhbmdlKC1gZGVsdGFHRlAgPC0+IFJGUGAsIEdGUCkgJT4lIGhlYWQoMTAwMCkgJT4lIERUOjpkYXRhdGFibGUocm93bmFtZXMgPSBGKQoKYGBgCgoKYGBge3J9CiMjIGRvZXMgUEFYNiByZWd1bGF0ZSBQQVg2PwojWWVzLCB5ZXMgaXQgZG9lcy4KCiNHRlAgc3BlY2lmaWMhCiAgCiMgYm90aCAlPiUgCiMgICAjIG9ubHkga2VlcCBvbmUgZ2VuZSBwZXIgbW90aWYKIyAgIGdyb3VwX2J5KG1vdGlmX2xvYywgc2FtcGxlLCBHZW5lKSAlPiUgCiMgICB0b3BfbigxLCBkaXN0YW5jZSkgJT4lIAojICAgdW5ncm91cCgpICU+JSAKIyAgICMga2VlcCB1cCB0byB0d28gZ2VuZXMgcGVyIG1vdGlmCiMgICBncm91cF9ieShtb3RpZl9sb2MsIHNhbXBsZSkgJT4lIAojICAgdG9wX24oMiwgZGlzdGFuY2UpICU+JSAKIyAgIHVuZ3JvdXAoKSAlPiUgCiMgICAjIGFycmFuZ2UgYnkgZ2VuZXMgbW9zdCBsaW5rZWQgdG8gbW90aWYgIAojICAgZ3JvdXBfYnkoR2VuZSwgc2FtcGxlKSAlPiUgCiMgICBzdW1tYXJpc2UoQ291bnQ9bigpLCBgTW90aWYgTG9jYXRpb25zYCA9IHBhc3RlKG1vdGlmX2xvYywgY29sbGFwc2U9JywgJykpICU+JSAKIyAgIGFycmFuZ2UoLUNvdW50KSAlPiUgCiMgICBmaWx0ZXIoR2VuZT09J1BBWDYnKQpgYGAKCmBgYAo=